/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "esp_check.h"
#include "driver/rmt_encoder.h"
#include <stdint.h>
#include "ir_rcs_encoder.h"
#include "rcs_api.h"

static const char *TAG = "ENCODE";

typedef struct
{
  rmt_encoder_t base;
  rmt_encoder_t *bytes_encoder;
  rmt_encoder_t *copy_encoder;
  int state;
} rmt_rcs_bullet_encoder_t;

static size_t rmt_encode_rcs_bullet(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
                                    const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
  rmt_rcs_bullet_encoder_t *rcs_bullet_encoder = __containerof(encoder, rmt_rcs_bullet_encoder_t, base);
  rmt_encoder_handle_t bytes_encoder = rcs_bullet_encoder->bytes_encoder;
  rmt_encoder_handle_t copy_encoder = rcs_bullet_encoder->copy_encoder;
  rmt_encode_state_t session_state = 0;
  rmt_encode_state_t state = 0;
  size_t encoded_symbols = 0;

  const rmt_symbol_word_t rcs_leading_symbol = {
      .level0 = 1,
      .duration0 = CUSTOM_LEADING_CODE_DURATION_0,
      .level1 = 0,
      .duration1 = CUSTOM_LEADING_CODE_DURATION_1};
  const rmt_symbol_word_t rcs_ending_symbol = {
      .level0 = 1,
      .duration0 = CUSTOM_ENDING_CODE_DURATION_0,
      .level1 = 0,
      .duration1 = 0x7FFF};
  switch (rcs_bullet_encoder->state)
  {
  case 0:
    encoded_symbols += copy_encoder->encode(copy_encoder, channel, &rcs_leading_symbol, sizeof(rcs_leading_symbol), &session_state);
    if (session_state & RMT_ENCODING_COMPLETE)
    {
      rcs_bullet_encoder->state = 1; // we can only switch to next state when current encoder finished
    }
    if (session_state & RMT_ENCODING_MEM_FULL)
    {
      state |= RMT_ENCODING_MEM_FULL;
      goto out; // yield if there's no free space to put other encoding artifacts
    }
  // fall-through
  case 1:
    encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, (uint8_t*)primary_data, data_size, &session_state);
    if (session_state & RMT_ENCODING_COMPLETE)
    {
      rcs_bullet_encoder->state = 2; // we can only switch to next state when current encoder finished
    }
    if (session_state & RMT_ENCODING_MEM_FULL)
    {
      state |= RMT_ENCODING_MEM_FULL;
      goto out; // yield if there's no free space to put other encoding artifacts
    }
  // fall-through
  case 2:
    encoded_symbols += copy_encoder->encode(copy_encoder, channel, &rcs_ending_symbol, sizeof(rcs_ending_symbol), &session_state);
    if (session_state & RMT_ENCODING_COMPLETE)
    {
      state |= RMT_ENCODING_COMPLETE;
      rcs_bullet_encoder->state = 0; // back to the initial encoding session
    }
    if (session_state & RMT_ENCODING_MEM_FULL)
    {
      state |= RMT_ENCODING_MEM_FULL;
      goto out; // yield if there's no free space to put other encoding artifacts
    }
  }
out:
  *ret_state = state;
  return encoded_symbols;
}

static esp_err_t rmt_del_rcs_bullet_encoder(rmt_encoder_t *encoder)
{
  rmt_rcs_bullet_encoder_t *rcs_bullet_encoder = __containerof(encoder, rmt_rcs_bullet_encoder_t, base);
  rmt_del_encoder(rcs_bullet_encoder->bytes_encoder);
  rmt_del_encoder(rcs_bullet_encoder->copy_encoder);
  free(rcs_bullet_encoder);
  return ESP_OK;
}

static esp_err_t rmt_rcs_bullet_encoder_reset(rmt_encoder_t *encoder)
{
  rmt_rcs_bullet_encoder_t *rcs_bullet_encoder = __containerof(encoder, rmt_rcs_bullet_encoder_t, base);
  rmt_encoder_reset(rcs_bullet_encoder->bytes_encoder);
  rmt_encoder_reset(rcs_bullet_encoder->copy_encoder);
  rcs_bullet_encoder->state = 0;
  return ESP_OK;
}

esp_err_t rmt_new_rcs_bullet_encoder(rmt_encoder_handle_t *ret_encoder)
{
  esp_err_t ret = ESP_OK;
  rmt_rcs_bullet_encoder_t *rcs_bullet_encoder = NULL;
  ESP_GOTO_ON_FALSE(ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
  rcs_bullet_encoder = calloc(1, sizeof(rmt_rcs_bullet_encoder_t));
  ESP_GOTO_ON_FALSE(rcs_bullet_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for musical score encoder");
  rcs_bullet_encoder->base.encode = rmt_encode_rcs_bullet;
  rcs_bullet_encoder->base.del = rmt_del_rcs_bullet_encoder;
  rcs_bullet_encoder->base.reset = rmt_rcs_bullet_encoder_reset;

  // different IR protocol might have its own timing requirements, following parameter is for NEC
  rmt_bytes_encoder_config_t bytes_encoder_config = {
      .bit0 = {
          .level0 = 1,
          .duration0 = CUSTOM_PAYLOAD_ZERO_DURATION_0, // T0H=560us
          .level1 = 0,
          .duration1 = CUSTOM_PAYLOAD_ZERO_DURATION_1, // T0L=560us
      },
      .bit1 = {
          .level0 = 1,
          .duration0 = CUSTOM_PAYLOAD_ONE_DURATION_0, // T1H=560us
          .level1 = 0,
          .duration1 = CUSTOM_PAYLOAD_ONE_DURATION_1, // T1L=1690us
      },
  };
  rmt_copy_encoder_config_t copy_encoder_config = {};
  rmt_new_copy_encoder(&copy_encoder_config, &rcs_bullet_encoder->copy_encoder);
  rmt_new_bytes_encoder(&bytes_encoder_config, &rcs_bullet_encoder->bytes_encoder);

  *ret_encoder = &rcs_bullet_encoder->base;
  return ESP_OK;
err:
  if (rcs_bullet_encoder)
  {
    if (rcs_bullet_encoder->bytes_encoder)
    {
      rmt_del_encoder(rcs_bullet_encoder->bytes_encoder);
    }
    if (rcs_bullet_encoder->copy_encoder)
    {
      rmt_del_encoder(rcs_bullet_encoder->copy_encoder);
    }
    free(rcs_bullet_encoder);
  }
  return ret;
}
